package com.nineclock.attendance.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nineclock.attendance.dto.AttendGroupDTO;
import com.nineclock.attendance.dto.AttendPunchDTO;
import com.nineclock.attendance.dto.AttendPunchUserWholeDayDTO;
import com.nineclock.attendance.dto.AttendsDTO;
import com.nineclock.attendance.enums.AttendEnums;
import com.nineclock.attendance.mapper.AttendPunchMapper;
import com.nineclock.attendance.pojo.AttendPunch;
import com.nineclock.attendance.service.AttendPunchService;
import com.nineclock.attendance.service.AttendanceGroupService;
import com.nineclock.attendance.utils.MyColumnStyle;
import com.nineclock.attendance.utils.PunchUtils;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.feign.SysUserFeign;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 * @Version 1.0
 */
@Service
public class AttendPunchServiceImpl implements AttendPunchService {

    @Autowired
    AttendPunchMapper attendPunchMapper;

    @Autowired
    AttendanceGroupService attendanceGroupService;


    public String generateExcelReport(Long companyId,List<AttendPunchDTO> attendPunchDTOS, String firstDay) {
        //4. 生成Excel
        String foldername = "D:/考勤数据/" + companyId; // yyyy-MM-dd
        String filename = foldername +"/" +"-"+firstDay.substring(0,7)+"-考勤数据.xlsx"; //2021-02-05

        if(!new File(foldername).exists()){
            new File(foldername).mkdirs();
        }

        EasyExcel.write(filename)
                .excelType(ExcelTypeEnum.XLSX)
                .head(handleHeader(attendPunchDTOS))
                .registerWriteHandler(new MyColumnStyle())
                .registerWriteHandler(horizontalCellStyleStrategy())
                .sheet("考勤数据")
                .doWrite(handleData(attendPunchDTOS));

        return filename;

    }

    @Override
    public void punch(AttendPunchDTO attendPunchDTO) {
        //转换为打卡对象
        AttendPunch attendPunch = BeanHelper.copyProperties(attendPunchDTO, AttendPunch.class);

        attendPunch.setPunchTime(new Date()); //打卡具体时间
        attendPunch.setPunchDateStr(DateTimeUtil.dateToStr(new Date(), "yyyy-MM-dd")); // 打卡的日期(yyyy-MM-dd)
        attendPunch.setCreateTime(new Date());
        attendPunch.setCompanyId(CurrentUserHolder.get().getCompanyId());
        attendPunch.setCompanyUserId(CurrentUserHolder.get().getCompanyUserId());

        //获取考勤组对象
        AttendGroupDTO attendGroup = attendanceGroupService.getAttendGroupByUserId();

        //是否有效范围内
        boolean punchAreaFlag = PunchUtils.validatePunchArea(attendPunchDTO, attendGroup);
        attendPunch.setAreaValid(punchAreaFlag);
        if(!punchAreaFlag){
            throw new NcException(ResponseEnum.PUNCH_INVALID_AREA);
        }

        //是否工作日打卡
        boolean workDayFlag = PunchUtils.validateWorkDay(attendGroup);
        if(!workDayFlag){
            throw new NcException(ResponseEnum.PUNCH_INVALID_DAY);
        }


        //上下午打卡是否有效
        boolean punchFlag = this.validatePunch(attendPunch);
        attendPunch.setEffectiveValid(punchFlag);
        if(punchFlag){
            //设置打卡类型
            PunchUtils.setPunchType(attendPunch, attendGroup);
        }



        attendPunchMapper.insert(attendPunch);
    }

    /**
     * 获取一整天的 某员工的 打卡记录
     * @return
     */
    @Override
    public AttendPunchUserWholeDayDTO queryPunchRecord() {
        //获取员工id
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();

        AttendPunchUserWholeDayDTO wholeDayDTO = new AttendPunchUserWholeDayDTO();
        //上午的有效打卡记录
        LambdaQueryWrapper<AttendPunch> morningQw = new LambdaQueryWrapper<>();
        morningQw.eq(AttendPunch::getCompanyId, CurrentUserHolder.get().getCompanyId());
        morningQw.eq(AttendPunch::getCompanyUserId, companyUserId);
        morningQw.eq(AttendPunch::getEffectiveValid, true);
        morningQw.eq(AttendPunch::getPunchOnOffWork, 1);

        AttendPunchDTO morningPunch = null;
        AttendPunch attendPunch = attendPunchMapper.selectOne(morningQw);
        if(attendPunch != null){
            morningPunch = BeanHelper.copyProperties(attendPunch, AttendPunchDTO.class);
        }






        //下午的有效打卡记录
        AttendPunchDTO afterNoonPunch = new AttendPunchDTO();

        //上午的有效打卡记录
        LambdaQueryWrapper<AttendPunch> afterNoonQw = new LambdaQueryWrapper<>();
        afterNoonQw.eq(AttendPunch::getCompanyId, CurrentUserHolder.get().getCompanyId());
        afterNoonQw.eq(AttendPunch::getCompanyUserId, companyUserId);
        afterNoonQw.eq(AttendPunch::getEffectiveValid, true);
        afterNoonQw.eq(AttendPunch::getPunchOnOffWork, 2);

        AttendPunch afterNoonAttendPunch = attendPunchMapper.selectOne(afterNoonQw);
        if(afterNoonAttendPunch != null){
            afterNoonPunch = BeanHelper.copyProperties(afterNoonAttendPunch, AttendPunchDTO.class);
        }



        wholeDayDTO.setAttendPunchMorningResponse(morningPunch);
        wholeDayDTO.setAttendPunchAfterNoonResponse(afterNoonPunch);

        return wholeDayDTO;
    }

    public List<AttendPunchDTO> getAttendPunch(Long companyId, List<SysCompanyUserDTO> companyUserDTOList,String startTime , String endTime, List<String> dateStrList){
        List<AttendPunchDTO> attendPunchList = BeanHelper.copyWithCollection(companyUserDTOList, AttendPunchDTO.class);
        for (AttendPunchDTO attendPunchDTO : attendPunchList) {

            //查询有效范围内 某员工的 所有的有效打卡信息
            LambdaQueryWrapper<AttendPunch> attendPunchQw =new LambdaQueryWrapper<>();
            attendPunchQw.eq(AttendPunch::getCompanyId, companyId);
            attendPunchQw.eq(AttendPunch::getEffectiveValid, true);
            attendPunchQw.between(AttendPunch::getPunchDateStr,startTime, endTime );
            attendPunchQw.eq(AttendPunch::getCompanyUserId, attendPunchDTO.getId());
            //获取某员工的考勤列表
            List<AttendPunch> punchList = attendPunchMapper.selectList(attendPunchQw);
            //对查询到的考勤列表分组： 按照日期分组
            Map<String, List<AttendPunch>> listMap =
                    punchList.stream().collect(Collectors.groupingBy(AttendPunch::getPunchDateStr));


            //创建考勤信息列表
            List<AttendsDTO> attendsDTOS = new ArrayList<>();
            //根据日期 获取考勤列表
            for (String dateStr : dateStrList) {
                List<AttendPunch> attendPunches = listMap.get(dateStr);
                //正常/正常  迟到/早退
                String attendStatus = this.handleAttendStatus(attendPunches);
                //创建每天的打卡信息对象
                AttendsDTO attendsDTO = new AttendsDTO();
                attendsDTO.setAttendDate(dateStr);
                attendsDTO.setAttendStatus(attendStatus);
                //添加到集合中
                attendsDTOS.add(attendsDTO);
            }

            //设置每天的打卡记录信息
            attendPunchDTO.setAttendsList(attendsDTOS);
        }
        return attendPunchList;
    }

    @Autowired
    SysUserFeign userFeign;
    @Override
    public List<AttendPunchDTO> queryMember(String startTime, String endTime) {
        //获取指定范围内的 日期列表
        List<Date> dateList = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, "yyyy-MM-dd");
        //转换日期字符串列表
        List<String> dateStrList = dateList.stream().map(date -> {
            String dateStr = DateTimeUtil.dateToStr(date, "yyyy-MM-dd");
            return dateStr;
        }).collect(Collectors.toList());

        //获取所有的员工信息
        List<SysCompanyUserDTO> companyUserDTOList = userFeign.findAllCompanyUser().getData();
        //转换为 考勤对象
        if (CollectionUtil.isNotEmpty(companyUserDTOList)){
            return this.getAttendPunch(CurrentUserHolder.get().getCompanyId(), companyUserDTOList, startTime, endTime, dateStrList);
        }


        return new ArrayList<>();
    }

    @Override
    public void export(String startTime, String endTime) throws IOException {
        //查询指定时间段内的考勤数据
        List<AttendPunchDTO> attendPunchDTOList = this.queryMember(startTime, endTime);


        //设置文件下载的头信息
        HttpServletResponse response =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setHeader("Content-disposition", "attachment;filename=Attendance.xlsx");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("utf-8");

        //写入Excel
        EasyExcel.write(response.getOutputStream())
                .excelType(ExcelTypeEnum.XLS) //文件的类型
                .head(handleHeader(attendPunchDTOList)) //操作 表头
                .registerWriteHandler(new MyColumnStyle()) //设置列宽
                .registerWriteHandler(horizontalCellStyleStrategy()) //设置水平居中
                .sheet("考勤记录") //excel的工作表名称
                .doWrite(handleData(attendPunchDTOList)); //操作 excel中的数据的

    }

    /**
     * 操作 考勤数据的
     * @param attendPunchDTOList
     * @return
     */
    private List<List<String>> handleData(List<AttendPunchDTO> attendPunchDTOList) {
        List<List<String>> data = new ArrayList<>();
        //每个员工 应该对应 一行数据， 每行数据应该 是一个List集合
        for (AttendPunchDTO attendPunchDTO : attendPunchDTOList) {
            List<String> list = new ArrayList<>();
            //添加数据
            //添加固定字段
            list.add(attendPunchDTO.getUserName());
            list.add(attendPunchDTO.getWorkNumber());
            list.add(attendPunchDTO.getDepartmentName());
            list.add(attendPunchDTO.getPost());
            //添加 非固定字段(每天的考勤情况)
            List<AttendsDTO> attendsList = attendPunchDTO.getAttendsList();
            if(CollectionUtil.isNotEmpty(attendsList)){
                for (AttendsDTO attendsDTO : attendsList) {
                    list.add(attendsDTO.getAttendStatus());
                }
            }


            //组装数据
            data.add(list);
        }

        return data;
    }

    /**
     * 操作excel表头信息的
     * @param attendPunchDTOList
     * @return
     */
    private List<List<String>> handleHeader(List<AttendPunchDTO> attendPunchDTOList) {
        List<List<String>> list = new ArrayList<>();
        //固定表头
        list.add(Arrays.asList("姓名"));
        list.add(Arrays.asList("工号"));
        list.add(Arrays.asList("部门"));
        list.add(Arrays.asList("职位"));
        //不固定表头(日期部分)
        if(CollectionUtil.isNotEmpty(attendPunchDTOList)){
            //取出一个人的考勤记录
            AttendPunchDTO punchDTO = attendPunchDTOList.get(0);

            //获取 每天的打卡情况
            List<AttendsDTO> attendsList = punchDTO.getAttendsList();
            for (AttendsDTO attendsDTO : attendsList) {
                String attendDate = attendsDTO.getAttendDate();
                list.add(Arrays.asList(attendDate));
            }
        }

        return list;
    }

    public  HorizontalCellStyleStrategy horizontalCellStyleStrategy() {
        WriteCellStyle cellStyle = new WriteCellStyle();
        //设置字体居中
        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //设置字体样式
        WriteFont font = new WriteFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short)12);
        cellStyle.setWriteFont(font);

        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(cellStyle, cellStyle);

        return styleStrategy;
    }

    //处理 考勤的状态
    private String handleAttendStatus(List<AttendPunch> punches) {
        String amAttendStatus = "缺卡";//上午 - 考勤
        String pmAttendStatus = "缺卡";//下午 - 考勤

        if(CollectionUtil.isNotEmpty(punches)){
            for (AttendPunch punch : punches) {
                if(punch.getPunchOnOffWork() == AttendEnums.NOON_TYPE_MORNING.value()){
                    amAttendStatus = this.getPushDesc(punch.getMorningPunchType());
                }else if (punch.getPunchOnOffWork() == AttendEnums.NOON_TYPE_AFTERNOON.value()){
                    pmAttendStatus = this.getPushDesc(punch.getAfternoonPunchType());
                }
            }
        }

        return amAttendStatus+"/"+pmAttendStatus;
    }


    private String getPushDesc(Integer morningPunchType) {
        String desc = "";
        switch (morningPunchType){
            case 1:
                desc = AttendEnums.PUNCH_TYPE_OK.toString();
                break;
            case 2:
                desc = AttendEnums.PUNCH_TYPE_LATE.toString();
                break;
            case 3:
                desc = AttendEnums.PUNCH_TYPE_EARLY.toString();
                break;
            case 4:
                desc = AttendEnums.PUNCH_TYPE_STAYALAY.toString();
                break;
            case 5:
                desc = AttendEnums.PUNCH_TYPE_NOT_WORK_DAY.toString();
                break;
            default:
                break;
        }
        return desc;
    }

    /**
     * 1. 查询打卡记录，如果没有查询到，无论何时打卡都是有效的
     * 2. 查询到了
     *  如果是上午打卡， 则无效
     *  如果是下午打卡， 以最后一次打卡记录为准, 之前打卡记录无效
     * @param attendPunch
     * @return
     */
    private boolean validatePunch(AttendPunch attendPunch) {
        LambdaQueryWrapper<AttendPunch> punchQw = new LambdaQueryWrapper<>();

        punchQw.eq(AttendPunch::getPunchOnOffWork, attendPunch.getPunchOnOffWork());
        punchQw.eq(AttendPunch::getCompanyId, CurrentUserHolder.get().getCompanyId());
        punchQw.eq(AttendPunch::getCompanyUserId, CurrentUserHolder.get().getCompanyUserId());
        punchQw.eq(AttendPunch::getPunchDateStr , attendPunch.getPunchDateStr());

        Integer count = attendPunchMapper.selectCount(punchQw);
        //查询记录为0条， 打卡必须有效
        if(count == 0){
            attendPunch.setEffectiveValid(true);
        }else{
            //上午第一次打卡有效
            if(attendPunch.getPunchOnOffWork() == 1){
                attendPunch.setEffectiveValid(false);
            }else{
                //下午打卡最后一次有效
                attendPunch.setEffectiveValid(true);
                //更新前面打卡无效
                AttendPunch updateAttendPunch = new AttendPunch();
                updateAttendPunch.setEffectiveValid(false);
                attendPunchMapper.update(updateAttendPunch, punchQw);

            }


        }

        return attendPunch.getEffectiveValid();
    }

}
